﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMathLibrary
{
    /// <summary>
    /// This class represents a part or a component of a linear spline.
    /// </summary>
    [Serializable]
    public class LinearSplineComponent : AbstractSplineComponent, ILinearSplineComponent
    {
        /// <summary>
        /// The linear function, which defines the splinepart.
        /// </summary>
        private LinearFunction linearFunction;

        /// <summary>
        /// Initializes a new instance of the <see cref="LinearSplineComponent"/> class.
        /// </summary>
        /// <param name="m">The slope of the linear function.</param>
        /// <param name="n">The translation constant slope of the linear function.</param>
        /// <param name="startX">The start value of the domain for this splinepart.</param>
        /// <param name="endX">The end value of the domain for this splinepart.</param>
        public LinearSplineComponent(double m, double n, double startX, double endX)
        {
            this.linearFunction = new LinearFunction(m, n);

            if (endX < startX)
            {
                this.start = new Point2D(endX, this.linearFunction.SolveAt(endX));
                this.end = new Point2D(startX, this.linearFunction.SolveAt(startX));
            }
            else
            {
                this.start = new Point2D(startX, this.linearFunction.SolveAt(startX));
                this.end = new Point2D(endX, this.linearFunction.SolveAt(endX));
            }            
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="LinearSplineComponent"/> class.
        /// </summary>
        /// <param name="function">The linear function, which describes the current splinepart.</param>
        /// <param name="startX">The start value of the domain for this splinepart.</param>
        /// <param name="endX">The end value of the domain for this splinepart.</param>
        public LinearSplineComponent(LinearFunction function, double startX, double endX)
        {
            if (function == (LinearFunction) null)
            {
                throw new ArgumentNullException("function");
            }

            this.linearFunction = new LinearFunction(function.Slope, function.TranslationConstant);

            if (endX < startX)
            {
                this.start = new Point2D(endX, this.linearFunction.SolveAt(endX));
                this.end = new Point2D(startX, this.linearFunction.SolveAt(startX));
            }
            else
            {
                this.start = new Point2D(startX, this.linearFunction.SolveAt(startX));
                this.end = new Point2D(endX, this.linearFunction.SolveAt(endX));
            }     
        }     

        /// <summary>
        /// Initializes a new instance of the <see cref="LinearSplineComponent"/> class.
        /// </summary>
        /// <param name="start">The start point of the domain for this splinepart.</param>
        /// <param name="end">The endpoint of the domain for this splinepart.</param>
        public LinearSplineComponent(Point2D start, Point2D end)
        {
            if (start == (Point2D) null)
            {
                throw new ArgumentNullException("start");
            }

            if (end == (Point2D) null)
            {
                throw new ArgumentNullException("end");
            }

            if (end.X < start.X)
            {
                this.start = end;
                this.end = start;
            }
            else
            {
                this.start = start;
                this.end = end;
            }

           LinearFunctionInterpolation interpolation = new LinearFunctionInterpolation(this.start,this.end);
           
            this.linearFunction = interpolation.Interpolate();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="LinearSplineComponent"/> class.
        /// </summary>
        /// <param name="function">The linear function, which describes the current splinepart.</param>
        /// <param name="start">The start point of the domain for this splinepart.</param>
        /// <param name="end">The endpoint of the domain for this splinepart.</param>
        public LinearSplineComponent(LinearFunction function, Point2D start, Point2D end)
        {
            if (function == (LinearFunction) null)
            {
                throw new ArgumentNullException("function");
            }

            if (start == (Point2D) null)
            {
                throw new ArgumentNullException("start");
            }

            if (end == (Point2D) null)
            {
                throw new ArgumentNullException("end");
            }

            if (end.X < start.X)
            {
                this.start = end;
                this.end = start;
            }
            else
            {
                this.start = start;
                this.end = end;
            }

            this.linearFunction = new LinearFunction(function.Slope, function.TranslationConstant);
        }

        /// <summary>
        /// Gets or sets the linear function of the splinepart.
        /// </summary>
        /// <value>The linear function of the splinepart.</value>
        public LinearFunction LinearFunction
        {
            get { return linearFunction; }
            set { linearFunction = value; }
        }

        /// <summary>
        /// Copies the current instance of a LinearSplineComponent.
        /// </summary>
        /// <returns>A copy of the current instance.</returns>
        public LinearSplineComponent Copy()
        {
            return new LinearSplineComponent(this.linearFunction,this.start, this.end);
        }

        /// <summary>
        /// Determines whether [the current LinearSplineComponent is defined for] [the specified x value].
        /// </summary>
        /// <param name="x">The x value to check.</param>
        /// <returns>
        /// 	<c>true</c> if [the current LinearSplineComponent is defined for] [the specified x value]; otherwise, <c>false</c>.
        /// </returns>
        public bool IsDefinedFor(double x)
        {
            return ((this.start.X <= x) && (x <= this.end.X));
        }

        /// <summary>
        /// Compares the current instance to another LinearSplineComponent.
        /// </summary>
        /// <param name="compare">The LinearSplineComponent to compare.</param>
        /// <returns>True if the components are even otherwise, false.</returns>
        public bool CompareTo(LinearSplineComponent compare)
        {
            if (compare == (LinearSplineComponent)null)
                throw new ArgumentNullException("compare");

            return ((this.linearFunction == compare.LinearFunction) && (this.start == compare.Start)
                    && (this.end == compare.End));
        }
    }
}